<!DOCTYPE html>
<html>
<head>
    <title>16kHz实时音频传输</title>
</head>
<body>
    <button id="startBtn">开始录音</button>
    <button id="stopBtn" disabled>停止录音</button>
    <div id="status">状态：未连接</div>

<script>
const wsUrl = 'ws://localhost:8080/ws';
const TARGET_SAMPLE_RATE = 16000;
let mediaStream;
let websocket;
let audioContext;
let source;
let processor;
let status = document.getElementById('status');
let startBtn = document.getElementById('startBtn');
let stopBtn = document.getElementById('stopBtn');

// 新版：带采样率约束的音频获取
async function getAudioStream() {
    const constraints = {
        audio: {
            sampleRate: TARGET_SAMPLE_RATE,
            channelCount: 1,
            echoCancellation: false,
            noiseSuppression: false,
            autoGainControl: false
        }
    };
    
    try {
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        const track = stream.getAudioTracks()[0];
        const settings = track.getSettings();
        
        // 实际采样率验证
        if(settings.sampleRate !== TARGET_SAMPLE_RATE) {
            console.warn(`设备不支持${TARGET_SAMPLE_RATE}Hz，实际采样率：${settings.sampleRate}Hz`);
            return { stream, needResample: true };
        }
        return { stream, needResample: false };
    } catch(error) {
        throw new Error('麦克风访问失败: ' + error.message);
    }
}

// 初始化WebSocket（返回Promise）
function initWebSocket() {
    return new Promise((resolve, reject) => {
        websocket = new WebSocket(wsUrl);
        
        websocket.onopen = () => {
            status.textContent = '状态：已连接服务器';
            resolve();
        };
        
        websocket.onerror = (error) => {
            reject('WebSocket连接失败: ' + error.message);
        };
    });
}

// 创建重采样处理器
function createResampler(source, inputRate, outputRate) {
    const bufferSize = 4096;
    const resampler = new OfflineAudioContext({
        numberOfChannels: 1,
        length: Math.ceil(bufferSize * outputRate / inputRate),
        sampleRate: outputRate
    });

    const cloneSource = resampler.createBufferSource();
    cloneSource.buffer = source.buffer;
    cloneSource.connect(resampler.destination);
    cloneSource.start();
    
    return resampler;
}

async function initAudioProcessing(needResample) {
    try {
        const { stream } = await getAudioStream();
        mediaStream = stream;
        
        audioContext = new (window.AudioContext || window.webkitAudioContext)();
        source = audioContext.createMediaStreamSource(stream);
        
        // 采样率处理
        let processingNode = source;
        if(needResample) {
            const resampler = createResampler(source, audioContext.sampleRate, TARGET_SAMPLE_RATE);
            processingNode = resampler;
        }

        processor = audioContext.createScriptProcessor(2048, 1, 1);
        processingNode.connect(processor);
        processor.connect(audioContext.destination);

        processor.onaudioprocess = (event) => {
            if (!websocket || websocket.readyState !== WebSocket.OPEN) return;
            
            const audioData = event.inputBuffer.getChannelData(0);
            const buffer = convertFloat32ToInt16(audioData);
            websocket.send(buffer);
            console.log(buffer);
        };
        
        status.textContent = '状态：录音中...';
        stopBtn.disabled = false;
        startBtn.disabled = true;
    } catch(error) {
        console.error(error);
        status.textContent = error.message;
        stopAll();
    }
}

function convertFloat32ToInt16(buffer) {
    const int16Buffer = new Int16Array(buffer.length);
    for (let i = 0; i < buffer.length; i++) {
        int16Buffer[i] = Math.min(1, buffer[i]) * 0x7FFF;
    }
    return int16Buffer.buffer;
}

function stopAll() {
    if (mediaStream) mediaStream.getTracks().forEach(t => t.stop());
    if (processor) processor.disconnect();
    if (source) source.disconnect();
    if (audioContext) audioContext.close();
    if (websocket) websocket.close();
    
    status.textContent = '状态：已停止';
    startBtn.disabled = false;
    stopBtn.disabled = true;
}

// 事件监听
startBtn.addEventListener('click', async () => {
    try {
        startBtn.disabled = true;
        status.textContent = '正在连接服务器...';
        
        // 顺序：先建立WebSocket连接
        await initWebSocket();
        
        // 获取音频流并检查采样率
        const { stream, needResample } = await getAudioStream();
        await initAudioProcessing(needResample);
        
    } catch(error) {
        status.textContent = error;
        stopAll();
    }
});

stopBtn.addEventListener('click', stopAll);
</script>
</body>
</html>
